﻿namespace IBTrader.Indicator.Oscillator
{
    using IBTrader.Modules.Read;
    using IBTrader.Charts;
    using System;

    class Aroon : BaseUpDown
    {
        private readonly PricesAroon Prices;
        internal Aroon(Worker worker, int interval = 60) : base(worker, 1, 2) { Prices = new PricesAroon(interval); }
        public override void Add(object _, Line line)
        {
            Prices.Add(line.Price);
            OnUp(line, Prices.Distance(Prices.MaxIndex));
            OnDown(line, Prices.Distance(Prices.MinIndex));
            OnHit(line, Prices.Distance(Prices.MaxIndex) + (Prices.Distance(Prices.MinIndex) - Prices.Length));
        }
        class PricesAroon : Prices
        {
            double max = double.MinValue, min = double.MaxValue;
            internal int MaxIndex = -1, MinIndex = -1;
            public PricesAroon(int length) : base(length) { }
            internal new double Add(double price)
            {
                base.Add(price);
                Reset(ref MaxIndex, ref max, double.MinValue, (x, y) => x > y);
                Reset(ref MinIndex, ref min, double.MaxValue, (x, y) => x < y);
                Set(ref MaxIndex, ref max, (x, y) => x >= y);
                Set(ref MinIndex, ref min, (x, y) => x <= y);
                return price;
            }
            internal double Distance(int distance)
            {
                return (Index - distance + Length) % Length;
            }
            private void Reset(ref int index, ref double price, double init, Func<double, double, bool> f)
            {
                if (index == Index)
                {
                    price = init;
                    for (int i = Index + Length; i > Index; i--)
                    {
                        var _index = i % Length;
                        if (f(this[_index], price))
                        {
                            price = this[_index];
                            index = _index;
                        }
                    }
                }
            }
            private void Set(ref int index, ref double price, Func<double, double, bool> f)
            {
                if (f(this[Index], price))
                {
                    price = this[Index];
                    index = Index;
                }
            }
        }
    }
}
